package com.myself.ssoserver.config

import com.fasterxml.jackson.databind.ObjectMapper
import com.myself.common.model.ResultData
import com.myself.common.model.ReturnCode
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.http.SessionCreationPolicy
import org.springframework.security.core.AuthenticationException
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.web.AuthenticationEntryPoint
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.access.AccessDeniedHandler
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
import org.springframework.stereotype.Component
import org.springframework.web.cors.CorsConfiguration
import org.springframework.web.cors.CorsConfigurationSource
import org.springframework.web.cors.UrlBasedCorsConfigurationSource
import java.io.IOException
import java.nio.charset.StandardCharsets


@Configuration
@EnableWebSecurity
open class SecurityConfig(
    private val jwtFilter: JwtAuthFilter,
    private val customAccessDeniedHandler: CustomAccessDeniedHandler,
    private val customAuthenticationEntryPoint: CustomAuthenticationEntryPoint,
) {

    @Bean
    open fun passwordEncoder(): PasswordEncoder {
        return BCryptPasswordEncoder()
    }

    @Bean
    open fun securityFilterChain(http: HttpSecurity, jwtAuthFilter: JwtAuthFilter): SecurityFilterChain {
        http
            .cors {corsConfigurationSource() }
            .csrf { it.disable() }
            .sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }
            .authorizeHttpRequests {
                it.requestMatchers("/api/auth/**").permitAll()
                it.anyRequest().authenticated()
            }
            .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter::class.java)
            .exceptionHandling()
            .accessDeniedHandler(customAccessDeniedHandler)
            .authenticationEntryPoint(customAuthenticationEntryPoint)
        return http.build()
    }
    @Bean
    open fun corsConfigurationSource(): CorsConfigurationSource {
        val corsConfig = CorsConfiguration()
        corsConfig.apply {
            allowedOriginPatterns = listOf("*")
//            allowedOrigins = listOf("http://localhost:8080")  // 允许的前端域名
            allowedMethods = listOf("GET", "POST", "PUT", "DELETE", "OPTIONS")  // 允许的请求方法
            allowedHeaders = listOf("*")  // 允许的请求头
            allowCredentials = true  // 是否允许发送凭证 (cookies)
        }

        val source = UrlBasedCorsConfigurationSource()
        source.registerCorsConfiguration("/**", corsConfig)
        return source
    }

}

@Component("customAccessDeniedHandler")
class CustomAccessDeniedHandler : AccessDeniedHandler {
    @Throws(IOException::class)
    override fun handle(
        request: HttpServletRequest?,
        response: HttpServletResponse?,
        accessDeniedException: org.springframework.security.access.AccessDeniedException?
    ) {
        val fail = ResultData.fail<String>(ReturnCode.RC403.code, ReturnCode.RC403.message)
        response?.writeJsonResponse(fail)
    }
}

@Component("customAuthenticationEntryPoint")
class CustomAuthenticationEntryPoint : AuthenticationEntryPoint {
    @Throws(IOException::class)
    override fun commence(
        request: HttpServletRequest?,
        response: HttpServletResponse,
        authException: AuthenticationException
    ) {
        val fail = ResultData.fail<String>(ReturnCode.RC401.code, ReturnCode.RC401.message)
        response.writeJsonResponse(fail)
    }
}
fun HttpServletResponse.writeJsonResponse(fail: ResultData<*>) {
    this.apply {
        resetBuffer() // 清除可能已写入的内容
        contentType = MediaType.APPLICATION_JSON_VALUE
        characterEncoding = StandardCharsets.UTF_8.name()

        val objectMapper = ObjectMapper()
        objectMapper.writeValue(writer, fail) // 将对象写入响应
        flushBuffer() // 刷新缓冲区
    }
}